#include "MirrorMaterial.h"

using namespace OBR;

std::shared_ptr<CudaProgram> MirrorMaterial::program;
CudaBuffer MirrorMaterial::sbt_data_payload;

MirrorMaterial::MirrorMaterial() : Material("shaders/Material/MirrorMaterial.cu")
{
    this->program_desc.callables.entryFunctionNameDC = "__direct_callable__mirror_material";
    this->program_desc.kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES;
    this->program_desc.flags = OPTIX_GEOMETRY_FLAG_NONE;
}

MirrorMaterial::~MirrorMaterial()
{
}

void MirrorMaterial::uploadVirtualProgramPayload(
    std::vector<std::shared_ptr<VirtualProgramEntry>> instances)
{
    // collect data
    MirrorMaterialData *data = new MirrorMaterialData[instances.size()];

    // set payload
    size_t payload_size = sizeof(MirrorMaterialData) * instances.size();
    this->sbt_data_payload.resize(payload_size);
    this->sbt_data_payload.upload(data, payload_size);

    delete[] data;
}

void MirrorMaterial::_buildFromConfig(const Json::Value &config)
{
    if (config["type"].asString().compare("mirror") != 0)
    {
        std::cerr << "wrong config value is sent to MirrorMaterial::_buildFromConfig" << std::endl;
        std::cerr << "got:" << config << std::endl;
        throw std::runtime_error("invalid parameter");
    }
}

CUdeviceptr MirrorMaterial::getPayloadDevicePtr()
{
    return MirrorMaterial::sbt_data_payload.d_pointer();
}

void MirrorMaterial::setProgram(std::shared_ptr<CudaProgram> prog)
{
    MirrorMaterial::program = prog;
}

std::shared_ptr<CudaProgram> MirrorMaterial::getProgram()
{
    return MirrorMaterial::program;
}

OptixProgramGroupOptions &MirrorMaterial::getProgramGroupOptions()
{
    return this->program_opt;
}

OptixProgramGroupDesc &MirrorMaterial::getProgramGroupDesc()
{
    this->program_desc.callables.moduleDC = this->cuda_module->optix_module;
    return this->program_desc;
}